package com.alesj.newsfeed.bool.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.alesj.newsfeed.bool.Condition;
import com.alesj.newsfeed.bool.Executor;
import com.alesj.newsfeed.bool.Token;
import com.alesj.newsfeed.bool.Tokenizer;
import com.alesj.newsfeed.bool.ConditionCreator;

/**
 * @author <a href="mailto:ales.justin@gmail.com">Ales Justin</a>
 */
public class ExecutorImpl<T> implements Executor<T> {

    private Tokenizer tokenizer;
    private Map<String, Condition<T>> map;

    public ExecutorImpl(ConditionCreator<T> creator) {
        this(new TokenizerImpl<T>(creator));
    }

    public ExecutorImpl(Tokenizer tokenizer) {
        this.tokenizer = tokenizer;
    }

    @SuppressWarnings("unchecked")
    public Set<T> execute(String query) {
        List<Token> tokens = tokenizer.transform(query);
        int size = tokens.size();
        if (size == 0) {
            return Collections.emptySet();
        }
        else if (size == 1) {
            return ((Condition<T>)tokens.get(0).value()).execute();
        }
        else {
            map = new HashMap<String, Condition<T>>();
            StringBuilder builder = new StringBuilder();
            int i = 0;
            for (Token token : tokens) {
                if (token instanceof OperatorToken)
                    builder.append(token);
                else {
                    builder.append(i);
                    map.put(String.valueOf(i), (Condition<T>)token.value());
                    i++;
                }
                builder.append(" ");
            }
            List<Condition<T>> ands = splitAnd(builder.toString());
            Set<T> result = ands.get(0).execute();
            for (i = 1; i < ands.size(); i++)
                result = And.INSTANCE.execute(result, ands.get(i).execute());
            return result;
        }
    }

    protected List<Condition<T>> splitAnd(String query) {
        String[] tokens = query.split("and|AND");
        List<Condition<T>> list = new ArrayList<Condition<T>>();
        for (String token : tokens) {
            list.add(splitOr(token));
        }
        return list;
    }

    protected Condition<T> splitOr(String token) {
        String[] tokens = token.split("or|OR");
        List<Condition<T>> conditions = new ArrayList<Condition<T>>();
        for (String t : tokens) conditions.add(map.get(t.trim()));
        return new OrCondition<T>(conditions);
    }
}
